package data;


/**
 * LinkedList All purpose data structure is a queue, stack, and linkedlist
 * also provides cycling of a current Object in list 
 * Author: Kyle Van Wie
 */
public class LinkedList<E> {
	Node<E> last;
	Node<E> first;
	Node<E> current;
	int size;

	public LinkedList() {
		last = null;
		first = null;
		current = null;
		size = 0;
	}
	public void addBack(E Obj) {
		if (size == 0) {
			last = new Node<E>(Obj,this);
			last.setNext(last);
			last.setPrev(last);
			first = last;
			current = last;

		} else {
			Node<E> temp = new Node<E>(Obj,this);
			temp.setPrev(last);
			last.setNext(temp);
			last = temp;
			last.setNext(first);
			first.setPrev(last);
		}
		size++;
	}
	public void addFront(E Obj) {
		if (size == 0) {

			first = new Node<E>(Obj,this);
			first.setNext(first);
			first.setPrev(first);
			last = first;
			current = first;
		} else {
			Node<E> temp = new Node<E>(Obj,this);
			temp.setNext(first);
			first.setPrev(temp);
			first = temp;
			first.setPrev(last);
			last.setNext(first);
		}
		size++;
	}
	public void addIndex(E Obj, int Index) {
		if (Index == 0) {
			addFront(Obj);
		} else if (Index == size) {
			addBack(Obj);
		} else if (Index >= 1 && Index < size) {
			Node<E> temp = first;
			for (int i = 0; i < Index; i++) {
				temp = temp.getNext();
			}
			Node<E> freshNode = new Node<E>(Obj,this);
			freshNode.setNext(temp);
			freshNode.setPrev(temp.getPrev());
			temp.getPrev().setNext(freshNode);
			temp.setPrev(freshNode);
			size++;

		}
	}
	public E removeFirst() {
		E element = null;
		if (size == 1) {
			element = first.getElement();
			first = null;
			last = null;
			current = null;
			size--;
		} else if (size != 0 && size != 1) {
			element = first.getElement();
			first.getPrev().setNext(first.getNext());
			first.getNext().setPrev(first.getPrev());
			first = first.getNext();
			size--;
		}
		return element;
	}
	public E removeLast() {
		E element = null;

		if (size == 1) {
			element = last.getElement();
			first = null;
			last = null;
			current = null;
			size--;
		} else if (size != 0 && size != 1) {
			element = last.getElement();
			last.getPrev().setNext(last.getNext());
			last.getNext().setPrev(last.getPrev());
			last = last.getPrev();
			size--;
		}
		return element;
	}
	public E removeCurrent() {
		E element = null;
		if(current.equals(first)){
			removeFirst();
			current = first;
		}
		else if(current.equals(last)){
			removeLast();
			current = last;
		}
		else if(size != 0 && size != 1){
			element = current.getElement();
			current.getPrev().setNext(current.getNext());
			current.getNext().setPrev(current.getPrev());
			current = current.getPrev();
			size--;
		}
		return element;
	}
	public E removeIndex(int Index) {

		E element = null;
		if (Index == 0) {
			element = removeFirst();
		} 
		else if (Index == size - 1) {
			element = removeLast();
		
		} 
		else if (Index > -1 && Index < size) {
			Node<E> temp = first;
			if (size == 1) {
				element = last.getElement();
				last = null;
				first = null;
				current = null;
				size--;
			} 
			else if (size > 1) {
				for (int i = 0; i < Index; i++) {
					temp = temp.getNext();
				}
				if(temp.equals(current)){
					return removeCurrent();
				}
				else{
					element = temp.getElement();
					temp.getPrev().setNext(temp.getNext());
					temp.getNext().setPrev(temp.getPrev());
					size--;
				}
			}
		}
		return element;
	}
	public int getSize() {
		return size;
	}
	public boolean isEmpty() {
		if (size == 0) {
			return true;
		}
		return false;
	}
	public E peekLast() {
		E element = null;
		if (size != 0) {
			element = last.getElement();
		}
		return element;
	}
	public E peekFirst() {
		E element = null;
		if (size != 0) {
			element = first.getElement();
		}
		return element;
	}
	public E peekIndex(int Index) {
		E element = null;
		if (Index == 0) {
			element = peekFirst();
		} else if (Index == size - 1) {
			element = peekLast();
		} else if (size != 0 && Index > -1 && Index < size - 1) {
			Node<E> temp = first;
			for (int i = 0; i < Index; i++) {
				temp = temp.getNext();
			}
			element = temp.getElement();
		}
		return element;
	}
	public E peekCurrent() {
		E element = null;
		if(size != 0){
			element = current.getElement();
		}
		return element;
	}
	public void next() {
		current = current.getNext();
		int count = 0;
		while(((current.getElement() instanceof LinkedList<?>) && (((LinkedList<E>)current.getElement()).isEmpty() || ((LinkedList<E>)current.getElement()).isEmptyTerminal())) && count < size){
			current = current.getNext();
			count++;
		}
	}
	public void prev() {
		current = current.getPrev();
		int count = 0;
		while(((current.getElement() instanceof LinkedList<?>) && (((LinkedList<E>)current.getElement()).isEmpty() || ((LinkedList<E>)current.getElement()).isEmptyTerminal())) && count < size){
			current = current.getPrev();
			count++;
		}
	}
	public void setCurrent(int Index) {
		if(Index >= 0 && Index < size){
			Node<E> temp = first;
			for (int i = 0; i < Index; i++) {
					temp = temp.getNext();
			}
			current = temp;
		}
	}
	public void setToFirst(){
		current = first;
	}
	public void setToLast(){
		current = last;
	}
	public boolean isFirst(){
		return current.equals(first);
	}
	public boolean isLast(){
		return current.equals(last);
	}
	public int getTerminalSize(){
		int TerminalSize = 0;
		Node<E> temp = first;
		for(int i = 0; i < size;i++){
			if(temp.getElement() instanceof LinkedList<?>){
				if(temp.getElement() != null){
					TerminalSize += ((LinkedList<?>)temp.getElement()).getTerminalSize();
				}
			}
			else{
				TerminalSize++;
			}
			temp = temp.getNext();
		}
		return TerminalSize;
	}
	public boolean find(Object o){
		Node<E> temp = first;
		for(int i = 0;i < size;i++){
			if(temp.getElement().equals(o)){
				return true;
			}
			temp = temp.getNext();
		}
		return false;
	}
	public E peek(Object o){
		Node<E> temp = first;
		for(int i = 0;i < size;i++){
			if(temp.getElement().equals(o)){
				return temp.getElement();
			}
			temp = temp.getNext();
		}
		return null;
	}
	public E remove(Object o){
		Node<E> temp = first;
		for(int i = 0;i < size;i++){
			if(temp.getElement().equals(o)){
				return removeIndex(i);
			}
			temp = temp.getNext();
		}
		return null;
	}
	public boolean isEmptyTerminal(){
		if(getTerminalSize() == 0){
			return true;
		}
		return false;
	}
}
